﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Text;
using System.Windows.Forms;

namespace Bono.PhotoSorter
{
	public static class Processing
	{
		private static DateTime GetDateTaken(string filePath)
		{

			//NOTE:  This version enhanced by suggestions from Alan Dukes.  THanks!
			DateTime result = DateTime.MinValue;

			try
			{
				using (Image image = Image.FromStream(File.OpenRead(filePath), true, false))
				{
					var item = image.PropertyItems.FirstOrDefault(l => l.Id == (int)Constants.PropertyIdTags.ExifDTOrig);
					if (item != null)
					{
						// Note:  Exif Dates are in the format "YYYY:MM:DD HH:MM:SS"
						string[] data = Encoding.UTF8.GetString(item.Value, 0, 19).Split(" :".ToCharArray(), 6);
						result = new DateTime(Int32.Parse(data[0]),
																	Int32.Parse(data[1]),
																	Int32.Parse(data[2]),
																	Int32.Parse(data[3]),
																	Int32.Parse(data[4]),
																	Int32.Parse(data[5]));

					}
				}
				return result;
			}
			catch
			{
				return result;
			}      

	
		}


		public static void CopyFolder(string sourceFolder, string destFolder, bool overwriteExisting = false, string searchPattern = "*.jpg")
		{
			// Method adapted from code found here:
			// http://www.csharp411.com/c-copy-folder-recursively/

			if (Directory.Exists(destFolder) == false)
			{
				Directory.CreateDirectory(destFolder);
			}

			string name;
			string dest;
			string[] files = Directory.GetFiles(sourceFolder, searchPattern);
			foreach (string file in files)
			{
				name = Path.GetFileName(file);
				dest = Path.Combine(destFolder, name);
				File.Copy(file, dest, overwriteExisting);
			}

			string[] folders = Directory.GetDirectories(sourceFolder);
			foreach (string folder in folders)
			{
				name = Path.GetFileName(folder);
				dest = Path.Combine(destFolder, name);
				CopyFolder(folder, dest, overwriteExisting, searchPattern);
			}
		}


		/// <summary>
		/// Gets the new path to which the file will be copied
		/// </summary>
		/// <param name="date"></param>
		/// <param name="createDirectories"></param>
		/// <param name="targetRoot"></param>
		/// <returns></returns>
		private static string GetNewPath(DateTime date, bool createDirectories, string targetRoot)
		{
			string yearPath = Path.Combine(targetRoot, date.Year.ToString());
			string datePath = Path.Combine(yearPath, date.ToString(Constants.DateFormat));

			if (createDirectories)
			{
				if (Directory.Exists(yearPath) == false)
				{
					Directory.CreateDirectory(yearPath);
				}

				if (Directory.Exists(Path.Combine(yearPath, date.ToString(Constants.DateFormat))) == false)
				{
					Directory.CreateDirectory(datePath);
				}
			}
			return datePath;
		}


		/// <summary>
		/// This method is used to prompt the user for to select from options for file conflicts (based on target filename)
		/// </summary>
		/// <param name="sourceFileName">The conflicting file that is to be copied</param>
		/// <param name="targetFileName">The existing file that we are attempting to copy or move</param>
		/// <param name="applyAllAction"></param>
		private static void HandleConflict(string sourceFileName, string targetFileName,
			Constants.ResolveConflictAction applyAllAction = Constants.ResolveConflictAction.Unset)
		{
			if (applyAllAction == Constants.ResolveConflictAction.KeepBoth)
			{
				//Append a random filename to the target file
				File.Copy(sourceFileName, GetRandomFileName(targetFileName));
			}
			else if (applyAllAction == Constants.ResolveConflictAction.OverwriteExisting)
			{
				File.Copy(sourceFileName, targetFileName, true);
			}
			else if (applyAllAction == Constants.ResolveConflictAction.KeepExisting)
			{
				//Do nothing since the appropriate file is already in the target location
			}
			else
			{
				//The apply to all action is Unset
				ResolveConflict resolveDialog = new ResolveConflict(sourceFileName, targetFileName);
				if (resolveDialog.ShowDialog() == DialogResult.OK)
				{

					if (resolveDialog.ApplyToAll)
					{
						applyAllAction = resolveDialog.ResolveAction;
					}

					if (resolveDialog.ResolveAction == Constants.ResolveConflictAction.KeepExisting)
					{
						//Do nothing
					}
					else if (resolveDialog.ResolveAction == Constants.ResolveConflictAction.KeepBoth)
					{
						//Append a random filename to the target file
						File.Copy(sourceFileName, GetRandomFileName(targetFileName));
					}
					else if (resolveDialog.ResolveAction == Constants.ResolveConflictAction.OverwriteExisting)
					{
						File.Copy(sourceFileName, targetFileName, true);
					}
				}
			}
		}


		/// <summary>
		/// Used to avoid overwriting existing files... will create a random name to append to the file name
		/// </summary>
		/// <param name="initialPath"></param>
		/// <returns></returns>
		private static string GetRandomFileName(string initialPath)
		{
			string fileName = Path.GetFileNameWithoutExtension(initialPath);
			string tempName = Path.GetFileNameWithoutExtension(Path.GetRandomFileName());

			return Path.Combine(Path.GetDirectoryName(initialPath), fileName + "_" + tempName + Path.GetExtension(initialPath));
		}


		/// <summary>
		/// Handles the copying or moving of files listed in the listeview
		/// </summary>
		/// <param name="listItems">All listview items that have been displayed</param>
		/// <param name="fileAction"></param>
		/// <param name="doWorkEventArgs"></param>
		/// <param name="fileWorker"></param>
		public static void ProcessFiles(List<FileListItem> listItems, Constants.FileAction fileAction, DoWorkEventArgs doWorkEventArgs, BackgroundWorker fileWorker)
		{
			if (fileAction == Constants.FileAction.DoNothing)
			{
				fileWorker.ReportProgress(100);
				return;
			}


			int currentItem = 0;
			int conflictCount = 0;
			int totalCount = listItems.Count;
			foreach (FileListItem item in listItems)
			{
				if (fileWorker.CancellationPending)
				{
					return;
				}

				System.Diagnostics.Debug.Print("Source:  " + item.SourceFilePath);
				System.Diagnostics.Debug.Print("Target:  " + item.TargetFilePath);

				if (File.Exists(@item.TargetFilePath))
				{
					//Here let's do some checks to see if we can see if we really have a conflict, or exact duplicates.
					//If exact duplicates, just leave the one in the target directory and keep going.
					//We'll determine based on the file size... assuming if it's named the same and the same size, it's probably the same file.

					FileInfo target = new FileInfo(@item.TargetFilePath);
					FileInfo source = new FileInfo(@item.SourceFilePath);

					if (target.Length != source.Length)
					{
						conflictCount++;
						HandleConflict(item.SourceFilePath, item.TargetFilePath, (Constants.ResolveConflictAction) Properties.Settings.Default.ResolveConflictOption);
					}

				}
				else
				{
					if (fileAction == Constants.FileAction.Move)
					{
						File.Move(@item.SourceFilePath, @item.TargetFilePath);
					}
					else
					{
						File.Copy(@item.SourceFilePath, @item.TargetFilePath);
					}
				}
				
				currentItem++;
				
				double percentComplete = (double)currentItem / (double)listItems.Count;
				fileWorker.ReportProgress((int)(percentComplete * 100),
																	new FileProcessStatus { ConflictCount = conflictCount, CurrentFile = currentItem, TotalFiles = totalCount, CurrentListItem = item });

				

			}

		}


		public static void CleanupFolders(string sourcePath)
		{
			DirectoryInfo di = new DirectoryInfo(sourcePath);
			DirectoryInfo[] directories = di.GetDirectories();
			//List<DirectoryInfo> emptyList = new List<DirectoryInfo>();
			foreach (DirectoryInfo info in directories)
			{
				FileInfo[] files = info.GetFiles();
				if (files.Length == 0)
				{
					info.Delete();
					//emptyList.Add(info);
				}
			}
		}


		/// <summary>
		/// Builds a list of files contained in the source root (recursively)
		/// </summary>
		/// <param name="sourceRoot">The root directory to search for images.</param>
		/// <param name="searchPattern">A valid directory search pattern (i.e. "*.jpg" )</param>
		/// <returns>list of strings. Each is the full path and filename.</returns>
		public static List<string> BuildFileList(string sourceRoot, string searchPattern = "*.jpg")
		{
			if (Directory.Exists(sourceRoot))
			{
				List<string> sourceFilesList = new List<string>();
				sourceFilesList.AddRange(Directory.GetFiles(sourceRoot, searchPattern, SearchOption.AllDirectories));
				return sourceFilesList;
			}
			return new List<string>();
		}

		/// <summary>
		/// Builds a list of files contained in the source root (recursively)
		/// </summary>
		/// <param name="sourceRoot">The root directory to search for images.</param>
		/// <param name="targetRoot"></param>
		/// <param name="searchOption"></param>
		/// <param name="searchPattern">A valid directory search pattern (i.e. "*.jpg" )</param>
		/// <returns>list of strings. Each is the full path and filename.</returns>
		public static List<FileListItem> BuildFileListItems(string sourceRoot, string targetRoot, SearchOption searchOption, string searchPattern = "*.jpg")
		{
			if (Directory.Exists(sourceRoot))
			{
				List<FileListItem>  fileListItems = new List<FileListItem>();

				int index = 0;

				foreach (string file in Directory.GetFiles(sourceRoot, searchPattern, searchOption))
				{

					FileListItem item = new FileListItem
					                    	{
					                    		SourceFilePath = file,
					                    		DateTaken = GetDateTaken(file),
					                    		ListViewIndex = index
					                    	};

					item.TargetFilePath =
						Path.Combine(Processing.GetNewPath(item.DateTaken, true, targetRoot), Path.GetFileName(item.SourceFilePath));
					
					fileListItems.Add(item);

					index++;

				}

				return fileListItems;
			}

			return new List<FileListItem>();
		}

	}
}
